/**
 * @file ctrlzee_control.c (gateway)
 * @brief A Z-Stack module that provides an implementation of a control 
 *        endpoint on a gateway node. This endpoint functions as the hub that 
 *        receives and sends control messages to other nodes in the network.
 * @author Vamshi Ponnapalli
 */
/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "ZDObject.h"
#include "hal_assert.h"

#include "ctrlZee.h"
#include "hal_ethernet.h"
#include "eth_config.h"
#include "w5100.h"

/*******************************************************************************
 * Variable declarations
 ******************************************************************************/
// ----- Constants (macros) -----
#define CTRLZEE_CONTROL_OUT_CLUSTER_COUNT   1
#define CTRLZEE_CONTROL_IN_CLUSTER_COUNT    0

#define CTRLZEE_CONTROL_MAX_DEST_ADDR       2

// ----- Constants (variables) -----
//Output clusters.
const cId_t ctrlZee_controlOutClusters[CTRLZEE_CONTROL_OUT_CLUSTER_COUNT] =
{
  CTRLZEE_CLUSTER_ID_COMMAND
};

//Input clusters.
const cId_t ctrlZee_controlInClusters[];

//Simple descriptor
const SimpleDescriptionFormat_t ctrlZee_controlSimpleDesc =
{
  CTRLZEE_ENDPOINT_CONTROL,
  CTRLZEE_PROFILE_ID,
  CTRLZEE_DEVICE_ID_CONTROL,
  CTRLZEE_DEVICE_VERSION,
  CTRLZEE_FLAGS_DEFAULT,

  //Input clusters
  CTRLZEE_CONTROL_IN_CLUSTER_COUNT,
  (cId_t *)ctrlZee_controlInClusters,

  //Output clusters
  CTRLZEE_CONTROL_OUT_CLUSTER_COUNT,
  (cId_t *)ctrlZee_controlOutClusters
};

// ----- Global variables -----

//Endpoint descriptor
endPointDesc_t ctrlZee_controlEndpointDesc;

//Test message being sent to devices
byte ctrlZee_controlPingCounter=0;

//Task id of the ctrlzee module.
byte ctrlZee_controlTaskId;   

//Holds the current network state of the ctrlzee module.
devStates_t ctrlZee_controlNetworkState;

//Ctrl-Zee specific message counter.
byte ctrlZee_controlMessageId;

//Variable to hold the destination address for Zigbee communication
afAddrType_t ctrlZee_controlDestAddress[CTRLZEE_CONTROL_MAX_DEST_ADDR];

//Ctrl-Zee ping enable/disable
byte ctrlZee_controlEnablePing;

//Max Payload = 1 byte command  + CTRLZEE_HOST_MAX_PAYLOAD_SIZE payload
uint8 ctrlZee_control_payload[CTRLZEE_HOST_MAX_PAYLOAD_SIZE + 1];

/*******************************************************************************
 * Static function declarations (private to this file)
 ******************************************************************************/
static void ctrlZee_controlProcessZDOMessage(zdoIncomingMsg_t *inMsg);
static void ctrlZee_controlProcessIncomingMessage(afIncomingMSGPacket_t *packet);
static void ctrlZee_controlSendMessage(afAddrType_t* destAddress, 
                                       ctrlzee_peer_cmd_t *message, 
                                       byte size);
static void ctrlZee_controlProcessHostCommand(ctrlzee_host_cmd_t *hostCommand);

static void ctrlZee_controlSendPeerPing(void);
static void ctrlZee_controlSendPeerCommand(byte command,
                                                uint16 address, 
                                                byte arg);
static void ctrlZee_controlSendZDOMatchRequest(void);
static void ctrlZee_controlSendZDOSimpleDescRequest(uint16 address, 
                                                                 byte endpoint);

// !!--------------------- TO BE DELETED --------------------
static uint16 TEMP_getAddress(void);


/*******************************************************************************
 * Function implementations
 ******************************************************************************/
void ctrlZee_controlInit(byte taskId)
{
  //Basic initialization
  ctrlZee_controlTaskId = taskId;
  ctrlZee_controlNetworkState = DEV_INIT;
  ctrlZee_controlMessageId = 0;
  ctrlZee_controlEnablePing = 0;
  
  //Initialize destination address(es)
  for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
  {
    ctrlZee_controlDestAddress[index].addrMode = (afAddrMode_t)AddrNotPresent;
    ctrlZee_controlDestAddress[index].endPoint = 0;
    ctrlZee_controlDestAddress[index].addr.shortAddr = 0;
  }
  //Fill out the endpoint description.
  ctrlZee_controlEndpointDesc.endPoint = CTRLZEE_ENDPOINT_CONTROL;
  ctrlZee_controlEndpointDesc.task_id = &ctrlZee_controlTaskId;
  ctrlZee_controlEndpointDesc.simpleDesc
            = (SimpleDescriptionFormat_t *)&ctrlZee_controlSimpleDesc;
  ctrlZee_controlEndpointDesc.latencyReq = noLatencyReqs;

  //Register the endpoint description with the AF
  afRegister(&ctrlZee_controlEndpointDesc);
  
  //Set local IP address.
  hal_ethernet_setLocalIPAddress(ETHERNET_LOCAL_IP_ADDRESS);
  
  //Set subnet mask.
  hal_ethernet_setSubnetMask(ETHERNET_GATEWAY_IP_ADDRESS);
  
  //Set gateway address.
  hal_ethernet_setGatewayAddress(ETHERNET_SUBNET_MASK);
  
  //Set an event so that the processing routine is notified once the device
  //has reached steady state.
  osal_set_event(ctrlZee_controlTaskId, CTRLZEE_OSAL_EVENT_CONNECT_HOST);  
  
  //Register for ZDO messages.  
  ZDO_RegisterForZDOMsg(ctrlZee_controlTaskId, End_Device_Bind_rsp);
  ZDO_RegisterForZDOMsg(ctrlZee_controlTaskId, Match_Desc_rsp);
  ZDO_RegisterForZDOMsg(ctrlZee_controlTaskId, Simple_Desc_rsp);
}
/******************************************************************************/
UINT16 ctrlZee_controlProcessEvent(byte taskId, UINT16 events)
{
  afIncomingMSGPacket_t *packet;
  (void)taskId;  // Intentionally unreferenced parameter

  //Check if the a system event was raised.
  if (events & SYS_EVENT_MSG)
  {
    //Read the incoming message(s).
    packet = (afIncomingMSGPacket_t *)osal_msg_receive(ctrlZee_controlTaskId);

    //Loop through all incoming messages.
    while (packet)
    {
      switch (packet->hdr.event)
      {
          //Callback message from the ZDO layer.
        case ZDO_CB_MSG:
          ctrlZee_controlProcessZDOMessage((zdoIncomingMsg_t *)packet);
          break;
          
          //Confirmation of a message packet sent.
        case AF_DATA_CONFIRM_CMD:
          // Action taken when confirmation is received.
          if (((afDataConfirm_t*)packet)->hdr.status != ZSuccess)
          {
            //TODO: Do something here.
          }
          break;

          //Incoming message.
        case AF_INCOMING_MSG_CMD:
           ctrlZee_controlProcessIncomingMessage(packet);
          break;

          //Change of network state of the device.
        case ZDO_STATE_CHANGE:
          //Update network status.
          ctrlZee_controlNetworkState = (devStates_t)(packet->hdr.status);
          
          if ((ctrlZee_controlNetworkState == DEV_ZB_COORD)
              || (ctrlZee_controlNetworkState == DEV_ROUTER)
              || (ctrlZee_controlNetworkState == DEV_END_DEVICE))
          {
            // Start sending "the" message in a regular interval.
            osal_start_timerEx(ctrlZee_controlTaskId,
                               CTRLZEE_OSAL_EVENT_SEND_PING,
                               CTRLZEE_OSAL_PING_DURATION);
          }
          break;

        default:
          break;
      }

      //Release the memory.
      osal_msg_deallocate((uint8 *)packet);

      //Read next message in the chain.
      packet = (afIncomingMSGPacket_t *)osal_msg_receive(ctrlZee_controlTaskId);
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  /**
   * "Connect to host" event received.
   */
  if(events & CTRLZEE_OSAL_EVENT_CONNECT_HOST)
  {
    //Set remote IP address.
    hal_ethernet_setRemoteIPAddress(ETHERNET_GATEWAY_IP_ADDRESS);
    
    //Set remote port number.
    hal_ethernet_setRemotePortNumber(ETHERNET_REMOTE_PORT);
    
    //Set local port number.
    hal_ethernet_setLocalPortNumber(ETHERNET_LOCAL_PORT);
    
    //Open a connection with the remote host.
    if(hal_ethernet_connect(W5100_SOCK_MODE_UDP, 5)!=SUCCESS)
    {
      HAL_ASSERT(false);
    } 
      
    //Send hello message to host.
    hal_ethernet_transmit(CTRLZEE_MESSAGE_HOST_HELO, 
                          CTRLZEE_MESSAGE_HOST_HELO_SIZE);
   
    //Now that a connection has been established, set an event to poll for data.
    osal_set_event(ctrlZee_controlTaskId, CTRLZEE_OSAL_EVENT_CHECK_HOST_DATA);
    
    //Return unprocessed events.
    return (events ^ CTRLZEE_OSAL_EVENT_CONNECT_HOST);
  }
  
  /**
   * "Poll host for data" event received.
   */
  if(events & CTRLZEE_OSAL_EVENT_CHECK_HOST_DATA)
  {
    //Poll the ethernet device to see if data is available
    uint16 availableBytes = hal_ethernet_getAvailableDataSize();
    
    //Check if any data was available.
    while(availableBytes > 0)
    {
      uint16 payloadSize;
      byte header[8];
      
      //Read in the UDP header.
      hal_ethernet_receive((uint8*)header, 8);
      payloadSize = network_readWord(((uint8*)header+6));
     
      if(payloadSize <= CTRLZEE_HOST_MAX_PAYLOAD_SIZE)
      {
        //Now read in UDP payload
        hal_ethernet_receive(ctrlZee_control_payload, payloadSize);
        
        //Process the host command
        ctrlZee_controlProcessHostCommand(
                                 (ctrlzee_host_cmd_t*)ctrlZee_control_payload);
        
        //Available bytes are now reduced
        availableBytes -= (8 + payloadSize);        
      }
      else
      {
        //Data payload too big. Not handled yet.
        HAL_ASSERT(false);
      }
    }
        
    //Set a recurring event to poll for data at some predefined intervals.
    osal_start_timerEx(ctrlZee_controlTaskId,
                       CTRLZEE_OSAL_EVENT_CHECK_HOST_DATA,
                       CTRLZEE_OSAL_POLL_DURATION); 
    
    //Return unprocessed events.
    return (events ^ CTRLZEE_OSAL_EVENT_CHECK_HOST_DATA);
  }
  
  /**
   * "Send ping" event received.
   */
  if (events & CTRLZEE_OSAL_EVENT_SEND_PING)
  {
    if(ctrlZee_controlEnablePing != 0)
    {
      //Send a ping message.
      ctrlZee_controlSendPeerPing();
      
      //Setup to send a ping again after a delay.
      osal_start_timerEx(ctrlZee_controlTaskId,
                         CTRLZEE_OSAL_EVENT_SEND_PING,
                         CTRLZEE_OSAL_PING_DURATION);
    }
    // return unprocessed events
    return (events ^ CTRLZEE_OSAL_EVENT_SEND_PING);
  }
  
  // Discard unknown events
  return 0;
}
/******************************************************************************/
static void ctrlZee_controlProcessZDOMessage(zdoIncomingMsg_t *inMsg)
{
  switch (inMsg->clusterID)
  {
    case Match_Desc_rsp:
      {
        ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp(inMsg);
        if (pRsp)
        {
          //First find an open slot to store this address.
          afAddrType_t *destAddress = 0x00;          
          for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
          {
            if(ctrlZee_controlDestAddress[index].addrMode == afAddrNotPresent)
            {
              destAddress = &ctrlZee_controlDestAddress[index];
            }
            else if(ctrlZee_controlDestAddress[index].addr.shortAddr == 
                                                                  pRsp->nwkAddr)
            {
              destAddress = &ctrlZee_controlDestAddress[index];
              break;
            }
          }
          
          //Check if an open slot was found.
          if(destAddress != 0x00)
          {
            if (pRsp->status == ZSuccess && pRsp->cnt)
            {
              destAddress->addrMode = (afAddrMode_t)Addr16Bit;
              destAddress->addr.shortAddr = pRsp->nwkAddr;
              
              // Keep it simple, and pick up the first endpoint
              destAddress->endPoint = pRsp->epList[0];
              
              //Notify host that a match request completed
              byte *pData = (byte*)(&pRsp->nwkAddr);
              ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_INIT_RESP;
              ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
              ctrlZee_control_payload[2] = pData[1];
              ctrlZee_control_payload[3] = pData[0];
              hal_ethernet_transmit(ctrlZee_control_payload, 4);
            }
          }
          osal_mem_free(pRsp);
        }
      }
      break;
  case Simple_Desc_rsp:
    {
      ZDO_SimpleDescRsp_t resp;
      
      //Parse out the simple description.
      ZDO_ParseSimpleDescRsp(inMsg, &resp);
      
      if(resp.status == ZDP_SUCCESS)
      {
        //Notify host that a discover request completed successfully.
        /*
         * Total payload = command + status + address hi + address lo + endpoint
         *                + device id hi + device id low + input cluster count
         *                + output cluster count + total input/output clusters
         * Non variable payload is 9 bytes, plus a variable cluster value.
         */
        uint16 payloadSize = 9
                             + (resp.simpleDesc.AppNumInClusters * 2)
                             + (resp.simpleDesc.AppNumOutClusters * 2);
        uint16 index = 0;
        ctrlZee_control_payload[index++] = CTRLZEE_HOST_CMD_DISC_RESP;
        ctrlZee_control_payload[index++] = CTRLZEE_STATUS_OK;
        
        byte *pData = (byte*)(&resp.nwkAddr);
        ctrlZee_control_payload[index++] = pData[1];
        ctrlZee_control_payload[index++] = pData[0];
        
        ctrlZee_control_payload[index++] = resp.simpleDesc.EndPoint;
        
        pData = (byte*) (&resp.simpleDesc.AppDeviceId);
        ctrlZee_control_payload[index++] = pData[1];
        ctrlZee_control_payload[index++] = pData[0];
        
        ctrlZee_control_payload[index++] = resp.simpleDesc.AppNumInClusters;
        for(uint8 clusterIndex = 0; 
                  clusterIndex < resp.simpleDesc.AppNumInClusters;
                  clusterIndex++)
        {
          ctrlZee_control_payload[index++] = 
                                resp.simpleDesc.pAppInClusterList[clusterIndex];
        }
        
        ctrlZee_control_payload[index++] = resp.simpleDesc.AppNumOutClusters;
        for(uint8 clusterIndex = 0; 
                  clusterIndex < resp.simpleDesc.AppNumOutClusters;
                  clusterIndex++)
        {
          ctrlZee_control_payload[index++] = 
                              resp.simpleDesc.pAppOutClusterList[clusterIndex];
        }
        hal_ethernet_transmit(ctrlZee_control_payload, payloadSize);
        
        if(resp.simpleDesc.AppNumInClusters > 0)
        {
          osal_mem_free(resp.simpleDesc.pAppInClusterList);
        }
        if(resp.simpleDesc.AppNumOutClusters > 0)
        {
          osal_mem_free(resp.simpleDesc.pAppOutClusterList);
        }
      }
      else
      {
        //Notify host that a discover request failed.
        ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_DISC_RESP;
        ctrlZee_control_payload[1] = CTRLZEE_STATUS_FAILED;
        hal_ethernet_transmit(ctrlZee_control_payload, 2);
      }      
    }
    break;
  }
}
/******************************************************************************/
static void ctrlZee_controlProcessIncomingMessage(afIncomingMSGPacket_t *pkt)
{
  ctrlzee_peer_cmd_t* message;
    
  switch (pkt->clusterId)
  {
  case CTRLZEE_CLUSTER_ID_COMMAND:
    message = (ctrlzee_peer_cmd_t*)pkt->cmd.Data;
    switch(message->command)
    {
    case CTRLZEE_PEER_CMD_REPORT_STATUS:
      {
        ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_REPORT_DATA_RESP;
        ctrlZee_control_payload[1] = 1; //Payload size is 1
        ctrlZee_control_payload[2] = message->arg1;
        
        //Transmit the report to the host.
        hal_ethernet_transmit(ctrlZee_control_payload, 3);
      }
      break;
    }
    
    break;
  }
}
/******************************************************************************/
static void ctrlZee_controlSendMessage(afAddrType_t* destAddress, 
                                       ctrlzee_peer_cmd_t *message, 
                                       byte size)
{
  //Don't send message if the short address has not been initialized.
  if(destAddress->addrMode == afAddrNotPresent)
  {
    return;
  }
  if (AF_DataRequest(destAddress,
                     &ctrlZee_controlEndpointDesc,
                     CTRLZEE_CLUSTER_ID_COMMAND,
                     size,
                     (byte *)message,
                     &ctrlZee_controlMessageId,
                     AF_DISCV_ROUTE,
                     AF_DEFAULT_RADIUS) == afStatus_SUCCESS)
  {
    //TODO: Do something here.
  }
  else
  {
    //TODO: Do something here.
  }
}
/******************************************************************************/
static void ctrlZee_controlProcessHostCommand(ctrlzee_host_cmd_t *hostCommand)
{  
  switch(hostCommand->command)
  {
  case CTRLZEE_HOST_CMD_INIT:
    ctrlZee_controlSendZDOMatchRequest();
    break;
  case CTRLZEE_HOST_CMD_LIST:
    {
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_LIST_RESP;
      
      byte deviceCount=0;
      
      //Loop through all addresses cached on the control endpoint.
      for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
      {
        //Check if the address buffer has a valid address.
        if(ctrlZee_controlDestAddress[index].addrMode != afAddrNotPresent)
        {
          //Yes. Add it to the buffer.
          deviceCount++;
          byte *pData = (byte*)(&ctrlZee_controlDestAddress[index].
                                                                addr.shortAddr);
          ctrlZee_control_payload[deviceCount*2] = pData[1];
          ctrlZee_control_payload[(deviceCount*2)+1] = pData[0];
        }
      }
      //Set the device count.
      ctrlZee_control_payload[1] = deviceCount;
      hal_ethernet_transmit(ctrlZee_control_payload, 2+(deviceCount*2));
    }
    break;
  case CTRLZEE_HOST_CMD_DISC:
    {
      uint16 address = BUILD_UINT16(hostCommand->data[1], hostCommand->data[0]);
      byte endpoint = hostCommand->data[2];
      
      //Temp:
      address = TEMP_getAddress();
      endpoint = CTRLZEE_ENDPOINT_CONTROL;
      
      //Send out a discovery request for the specified address/endpoint
      ctrlZee_controlSendZDOSimpleDescRequest(address, endpoint);
    }
    break;
  case CTRLZEE_HOST_CMD_ONOFF:
    {
      uint16 address = BUILD_UINT16(hostCommand->data[1], hostCommand->data[0]);
      byte command = hostCommand->data[2];
      
      //Loop through all addresses cached on the control endpoint.
      for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
      {
        //Check if the address buffer has a valid address.
        if(ctrlZee_controlDestAddress[index].addrMode != afAddrNotPresent)
        {
          //Yes. Capture this address.
          address = ctrlZee_controlDestAddress[index].addr.shortAddr;
          break;
        }
      }
      //Send a command to the peer.
      ctrlZee_controlSendPeerCommand(CTRLZEE_PEER_CMD_ONOFF, 
                                          address, 
                                          command);
      //Notify host of the action.
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_ONOFF_RESP;
      ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
      ctrlZee_control_payload[2] = hostCommand->data[0];
      ctrlZee_control_payload[3] = hostCommand->data[1];
      ctrlZee_control_payload[4] = command;
      hal_ethernet_transmit(ctrlZee_control_payload, 5);
    }
    break;
  case CTRLZEE_HOST_CMD_REPORT_ONOFF:
    {
      uint16 address = BUILD_UINT16(hostCommand->data[1], hostCommand->data[0]);
      byte command = hostCommand->data[2];
      
      //Temp:
      address = TEMP_getAddress();
      
      //Send a command to the peer.
      ctrlZee_controlSendPeerCommand(CTRLZEE_PEER_CMD_REPORT_ONOFF,   
                                          address, 
                                          command);
      //Notify host of the action.
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_REPORT_ONOFF_RESP;
      ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
      ctrlZee_control_payload[2] = hostCommand->data[0];
      ctrlZee_control_payload[3] = hostCommand->data[1];
      ctrlZee_control_payload[4] = command;
      hal_ethernet_transmit(ctrlZee_control_payload, 5);
    }
    break;    
  case CTRLZEE_HOST_CMD_START_PING:
    {
      //Set the enable ping flag.
      ctrlZee_controlEnablePing = 1;
      
      //Set an event to renable periodic pings
      osal_set_event(ctrlZee_controlTaskId, CTRLZEE_OSAL_EVENT_SEND_PING);
   
      //Notify host of the action.
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_START_PING_RESP;
      ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
      hal_ethernet_transmit(ctrlZee_control_payload, 2);
    }
    break;
  case CTRLZEE_HOST_CMD_STOP_PING:
    {
      //Reset the enable ping flag.
      ctrlZee_controlEnablePing = 0;
      
      //Acknowledge the request to the host.
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_STOP_PING_RESP;
      ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
      
      hal_ethernet_transmit(ctrlZee_control_payload, 2);
    }
    break;
  case CTRLZEE_HOST_CMD_PEER_DISC:
    {
      uint16 address = BUILD_UINT16(hostCommand->data[1], hostCommand->data[0]);

      //Temp:
      address = TEMP_getAddress();
      
      //Send a command to a peer.
      ctrlZee_controlSendPeerCommand(CTRLZEE_PEER_CMD_DISCOVER,
                                     address,
                                     0x00);
                                     
      //Acknowledge the request to the host.
      ctrlZee_control_payload[0] = CTRLZEE_HOST_CMD_PEER_DISC_RESP;
      ctrlZee_control_payload[1] = CTRLZEE_STATUS_OK;
      
      hal_ethernet_transmit(ctrlZee_control_payload, 2);
    }
  }
}
/******************************************************************************/
static void ctrlZee_controlSendPeerPing(void)
{
  //Increment the ping counter.
  ctrlZee_controlPingCounter++;
  
  //CtrlZee message structure
  ctrlzee_peer_cmd_t message = {CTRLZEE_PEER_CMD_PING, 
                                  ctrlZee_controlPingCounter,
                                  0x00,
                                  0x00};
 
  // Send the ping message
  for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
  {
    ctrlZee_controlSendMessage(&ctrlZee_controlDestAddress[index], 
                               &message, 
                               4);
  }
}
/******************************************************************************/
static void ctrlZee_controlSendPeerCommand(byte command,
                                           uint16 address, 
                                           byte arg)
{ 
  //CtrlZee message structure
  ctrlzee_peer_cmd_t message = {command,
                                arg,
                                0x00,
                                0x00};
  
  //Destination address.
  afAddrType_t dstAddr;
  dstAddr.addrMode = (afAddrMode_t)Addr16Bit;
  dstAddr.addr.shortAddr = address;
  dstAddr.endPoint = CTRLZEE_ENDPOINT_CONTROL;
  
  //Send the message.
  ctrlZee_controlSendMessage(&dstAddr, 
                             &message, 
                             4);
}
/******************************************************************************/
static void ctrlZee_controlSendZDOMatchRequest(void)
{
  // Initiate a Match Description Request (Service Discovery)
  zAddrType_t dstAddr;
  dstAddr.addrMode = AddrBroadcast;
  dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR;
  ZDP_MatchDescReq(&dstAddr,
                   NWK_BROADCAST_SHORTADDR,
                   CTRLZEE_PROFILE_ID,
                   CTRLZEE_CONTROL_OUT_CLUSTER_COUNT, 
                   (cId_t *)ctrlZee_controlOutClusters,
                   CTRLZEE_CONTROL_IN_CLUSTER_COUNT, 
                   (cId_t *)ctrlZee_controlInClusters,
                   FALSE);
}
/******************************************************************************/
static void ctrlZee_controlSendZDOSimpleDescRequest(uint16 address, 
                                                                  byte endpoint)
{
  // Initiate a Simple description request
  zAddrType_t dstAddr;
  dstAddr.addrMode = Addr16Bit;
  dstAddr.addr.shortAddr = address;
  ZDP_SimpleDescReq(&dstAddr,
                   address,
                   endpoint,
                   FALSE);
}
/******************************************************************************/
static uint16 TEMP_getAddress(void)
{
  
  //Loop through all addresses cached on the control endpoint.
  for(uint8 index=0; index < CTRLZEE_CONTROL_MAX_DEST_ADDR; index++)
  {
    //Check if the address buffer has a valid address.
    if(ctrlZee_controlDestAddress[index].addrMode != afAddrNotPresent)
    {
      //Yes. Capture this address.
      return(ctrlZee_controlDestAddress[index].addr.shortAddr);
    }
  }
  
  return(0x000);
}
